package sostats.core.query;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;

import sostats.core.base.StatisticalContext;
import sostats.core.help.SostatsException;
import sostats.core.help.Tuple;

import com.google.common.base.Function;
import com.google.common.collect.Lists;

public class Query {
	private final SummaryType type;
	private final String summary;
	private final List<String> categories;
	private final Map<String,ConditionBuilder> conditions;
	private final Map<String,String[]> paramMap;
	private Map<String,String> joinings;
	public Query(String summary,SummaryType type){
		this.summary = summary;
		this.type = type;
		categories = new ArrayList<String>();
		conditions = new HashMap<String,ConditionBuilder>();
		paramMap = new HashMap<String, String[]>();
	}
	public Query condition(ConditionBuilder conditionBuiler,String[] params){
		conditions.put(conditionBuiler.getAttribute(),conditionBuiler);
		paramMap.put(conditionBuiler.getAttribute(),params);
		return this;
	}
	public Query category(String category){
		categories.add(category);
		return this;
	}
	public int dimension(){
		return categories.size();
	}
	public String getSummary(){
		return summary;
	}
	public List<String> getCategories(){
		return Lists.newArrayList(categories);
	}
	List<String> selectColumns(){
		ArrayList<String> res = Lists.newArrayList(type.operate(summary));
		res.addAll(categories);
		return res;
	}
	List<String> groupByColumns(){
		return Lists.newArrayList(categories);
	}
	List<ConditionBuilder> whereConditions(){
		return new ArrayList<ConditionBuilder>(conditions.values());
	}
	String fromTable(StatisticalContext context) throws SostatsException{
		ArrayList<String> columns = Lists.newArrayList(summary);
		columns.addAll(categories);
		for(String condition:conditions.keySet()){
			List<String> wheres = conditions.get(condition).build(paramMap.get(condition));
			if(wheres.size()>0) columns.add(condition);
		}
		List<String> tables = Lists.transform(columns, new Function<String,String>(){
			@Override
			public String apply(String input) {
				int index = input.lastIndexOf(StatisticalContext.CONNECTOR);
				return input.substring(0,index);
			}
		});
		joinings  = context.getJoiningTables(tables);
		return findRoot(joinings);
	}
	boolean isMultitable(){
		return joinings!=null&&joinings.size()>1;
	}
	List<Tuple<String,String>> joinTables(StatisticalContext context) throws SostatsException{
		if(!isMultitable()) throw new SostatsException("There's no joining tables!");
		ArrayList<Tuple<String,String>> res = new ArrayList<Tuple<String,String>>();
		Queue<String> queue = new LinkedList<String>();
		queue.offer(findRoot(joinings));
		while(queue.size()>0){
			String curTable = queue.poll();
			for(final Entry<String,String> entry:joinings.entrySet()){
				if(entry.getValue().equals(curTable)&&!entry.getKey().equals(curTable)){
					Tuple<String,String> keyTuple = context.getJoiningKeys(curTable,entry.getKey());
					final String condition = curTable+"."+keyTuple.getFirst()+"="
							+entry.getKey()+"."+keyTuple.getSecond();
					res.add(new Tuple<String,String>(){
						@Override
						public String getFirst() {
							return entry.getKey();
						}
						@Override
						public String getSecond() {
							return condition;
						}
					});
					queue.offer(entry.getKey());
				}
			}
		}
		return res;
	}
	public SummaryType getSummaryType(){
		return this.type;
	}
	public static enum SummaryType{
		COUNT,SUM;
		public String operate(String column){
			return this.name()+"("+column+")";
		}
	}
	private String findRoot(Map<String, String> maps) throws SostatsException{
		for(Entry<String,String> entry: maps.entrySet()){
			if(entry.getValue().equals(entry.getKey())) return entry.getKey();
		}
		throw new SostatsException("the joining table trees don't have root");
	}
	public String[] getParameters(String attribute) {
		return paramMap.get(attribute);
	}
}
